/**
 * Javascript file for list_view.html
 */


// categories which should be shown
var shownCategories = [];
var noneCategory = "[None]";
/* Page manipulation */
var eventsPerPage = 10; // how many events should be shown on one page
var shownPage = 1; // which page is currently shown (for events per page)
var numberOfPages = 1; // number of showable pages
var shownEvents = []; // all events which are shown (after category test)

/**
 * Called in the very beginning in onload of the html file.
 * 
 * @returns {undefined}
 */
function initialize() {
    // get all events
    retrieveAllAvailableEvents();
    
    // load events per page
    eventsPerPage = localStorage.getItem("eventsPerPage");
    if (eventsPerPage !== null) {
        document.getElementById("eventsPerPage").value = eventsPerPage;
    } else {
        eventsPerPage = 10;
    }
    
    // apply page
    applyPage();
}

/**
 * Shows all events with the given category.
 * 
 * @param {string} category
 * @returns {undefined}
 */
function showCategory(category) {
    if (shownCategories.indexOf(category) === -1) {
        // not contained
        if (shownCategories === null || shownCategories.length === null) {
            // first element
            shownCategories = [category];
        } else {
            // holds already an element
            shownCategories[shownCategories.length] = category;
        }
    }
    
    applyCategoryChange();
}

/**
 * Hides all events with the given category.
 * Note that events may have more than one category and may be displayed
 * because the other category is visible.
 * 
 * @param {type} category
 * @returns {undefined}
 */
function hideCategory(category) {
    var index = shownCategories.indexOf(category);
    if (index !== -1) {
        // contained - splice array to remove given category as a workaround
        shownCategories.splice(index, 1);
    }
    
    applyCategoryChange();
}

/**
 * Called as the user changes the shown categories.
 * 
 * Updates all events which should be shown to the user including color shemes.
 * 
 * The following algorithm will apply:
 * 1. Delete all children of the listContent
 * 2. Loop through all received events and check whether they are contained
 *  in a selected category
 * 3. If yes -> add it to the shown events array; Else go to the next element
 * 4. Show the first page.
 */
function applyCategoryChange() {
    
    // reset shown categories
    shownEvents = [];
    // eleminate events which do not have a shown category
    if (storage.events !== null) {
        // sort events according to their start date
        for (var i = 0; i < storage.events.length; i++) {
            if (storage.events[i].categories.length === 0 && shownCategories.indexOf(noneCategory) !== -1) {
                addToShownEvents(storage.events[i]);
            } else if (isEventCategoryContainedInCategoryList(storage.events[i], shownCategories)) {
                // event category is contained in the show category array
                addToShownEvents(storage.events[i]);
            }
        }
        
        // update the number of pages
        numberOfPages = Math.round(shownEvents.length / eventsPerPage + 0.49999999999999);
    }
    firstPage();
    applyPage();
}

/**
 * Helper for the applyCategoryChange() method.
 * Adds the given event to the shown category. The color will be chosen on how
 * important the event is. Red means it will start very soon and green means
 * that it is gone. Yellow means that it is running.
 * 
 * @param {Event} event The event to add to shown events.
 * 
 * @returns {undefined}
 */
function addToShownEvents(event) {

    var newPos = shownEvents.length;
    shownEvents[newPos] = new Object();
    shownEvents[newPos].event = event; // store event
    var color = "white";
    var endDate = new Date(event.end);
    var startDate = new Date(event.start);
    var now = Date.now();
    if (endDate < now) {
        // gone event
        color = "yellow";
    } else if (endDate > now && startDate < now) {
        // active event
        color = "lightgreen";
    } else {
        // upcoming event
        // mark red if it is on the next day
        if (startDate - now <= 1000 * 60 * 60 * 24) { // one day differences
            color = "red";
        } else {
            color = "lightsalmon";
        }
    }
    
    shownEvents[newPos].color = color; // store color
}

/**
 * Special method to test if one of the event categories is contained
 * in the 
 * 
 * @param {type} event
 * @param {type} categoryList
 * @returns {Boolean}
 */
function isEventCategoryContainedInCategoryList(event, categoryList) {
    for (var i = 0; i < event.categories.length; i++) {
        if (categoryList.indexOf(event.categories[i].name) !== -1) {
            // contained
            return true;
        }
    }
    return false;
}

/**
 * Checks if the given category name is contained in the given event.
 * To check this the function will loop through the categories array
 * of the event.
 * 
 * @param {type} categoryName
 * @param {type} event
 * @returns {undefined} True if contained. False if not.
 */
function isCategoryNameContainedInEvent(categoryName, event) {
    for (var i = 0; i < event.categories.length; i++) {
        if (event.categories[i].name === categoryName) {
            return true;
        }
    }
    return false;
}

/**
 * Adds given event to the event table.
 * 
 * @param {Event} event you want to add.
 * @param {Color} color of the row.
 * @returns {undefined}
 */
function addEvent(event, color) {
    // get the table which holds all list elements
    var content = document.getElementById("listContent");
    // create a new row
    var row = content.appendChild(document.createElement("tr"));
    row.style.backgroundColor = color;
    // create the category column
    var categoryCol = row.appendChild(document.createElement("td"));
    // create the name column
    row.appendChild(document.createElement("td")).appendChild(document.createTextNode(event.title));
    // create the start date column
    row.appendChild(document.createElement("td")).appendChild(document.createTextNode(formatDate(new Date(event.start))));
    // create the end date column
    row.appendChild(document.createElement("td")).appendChild(document.createTextNode(formatDate(new Date(event.end))));
    // create the location column
    row.appendChild(document.createElement("td")).appendChild(document.createTextNode(event.location));
    // create the organizer column - the link will send a mail to the organizer
    var organizer = row.appendChild(document.createElement("td")).appendChild(document.createElement("a"));
    organizer.appendChild(document.createTextNode(event.organizer));
    organizer.title = event.organizer;
    organizer.href = "mailto:" + event.organizer;
    // create the status column
    row.appendChild(document.createElement("td")).appendChild(document.createTextNode(event.status));
    // create the allday column (1 means Yes, 0 means no)
    row.appendChild(document.createElement("td")).appendChild(document.createTextNode((event.allday === "1" ? "Yes" : "No")));
    // create the webpage column
    var webpage = row.appendChild(document.createElement("td")).appendChild(document.createElement("a"));
    webpage.appendChild(document.createTextNode(event.webpage));
    webpage.title = event.webpage;
    webpage.href = event.webpage;
    // create the image column
    if (event.imageurl !== "") {
        // there is an image
        var image = document.createElement("img");
        image.setAttribute("src", event.imageurl);
        image.setAttribute("alt", event.imageurl);
        image.setAttribute("style", "height:150px");
        row.appendChild(document.createElement("td")).appendChild(image);
    } else {
        // create empty row
        row.appendChild(document.createElement("td"));
    }
    
    // create an edit button
    var editButton = document.createElement("input");
    editButton.setAttribute("type", "button");
    editButton.setAttribute("value", "Edit");
    editButton.setAttribute("name", "editButton");
    editButton.setAttribute("onClick", "editEvent(" + event.id + ")");
    row.appendChild(document.createElement("td")).appendChild(editButton);
    
    // create a delete button
    var deleteButton = document.createElement("input");
    deleteButton.setAttribute("type", "button");
    deleteButton.setAttribute("value", "Delete");
    deleteButton.setAttribute("name", "deleteButton");
    deleteButton.setAttribute("onClick", "deleteEvent(" + event.id + ")");
    row.appendChild(document.createElement("td")).appendChild(deleteButton);
    
    // extract categories
    var categories = event.categories;
    
    // write categories into the category column
    var categoryText = ""; // create category text by appending category names
    if (!categories || categories.length === 0) {
        categoryText = "[None]";
    } else {
        for (var i = 0; i < categories.length; i++) {
            categoryText += categories[i].name;
            if (i + 1 < categories.length) {
                // append category if necessary
                categoryText += ", ";
            }
        }
    }
    categoryCol.appendChild(document.createTextNode(categoryText));
    
}

/**
 * Called if the user clicks on the delete button of an event.
 * 
 * @param {String} eventId
 * @returns {undefined}
 */
function deleteEvent(eventId) {
    // the event id must be a string
    eventId = "" + eventId;
    if (confirm("Do you really want to delete \"" + storage.findEvent(eventId).title + "\"?")) {
        // send delete request
        var request = new XMLHttpRequest();
        request.open("POST", URL_START, true);
        var formData = new FormData();
        formData.append("user", USER);
        formData.append("action", "delete");
        formData.append("format", "json");
        formData.append("id", eventId);
        request.onreadystatechange = function() {
            if (request.readyState === 4 && request.status === 200) {
                retrieveAllAvailableEvents();
            } else if (request.readyState === 4) {
                alert("Could not delete " + storage.findEvent(eventId).title);
            }
        };
        request.send(formData);
    }
}


/**
 * Gets all events from the websever and calls the callback method availableEventsRetrieved
 * as they are retrieved.
 */
function retrieveAllAvailableEvents() {
    // make list request
    var request = new XMLHttpRequest();
    request.open("POST", URL_START, true);
    var formData = new FormData();
    formData.append("user", USER);
    formData.append("action", "list");
    formData.append("format", "json");
    request.onreadystatechange = function() {
        availableEventsRetrieved(request);
    };
    request.send(formData);
}

function retrieveAllAvailableCategories() {
    var request = new XMLHttpRequest();
    request.open("POST", URL_START, true);
    var formData = new FormData();
    formData.append("user", USER);
    formData.append("action", "list-categories");
    formData.append("format", "json");
    request.onreadystatechange = function() {
        availableCategoriesRetrieved(request);
    };
    request.send(formData);
}

/**
 * 
 * @param {XMLHttpRequest} request
 * @returns {undefined}
 */
function availableEventsRetrieved(request) {
    if (request.readyState === 4 && (request.status === 200 || request.status === 0)) {
        // store received events
        storage.events = JSON.parse(request.responseText).events.events;
        
        // sort events
        storage.events.sort(function(a, b) {
            a = new Date(a.start);
            b = new Date(b.start);
            return a > b ? -1 : a < b ? 1 : 0;
        });
        
        retrieveAllAvailableCategories();
        
    } else if (request.readyState === 4) {
        alert("ERROR while loading events - " + request.status);
    }
}

function availableCategoriesRetrieved(request) {
    if (request.readyState === 4 && (request.status === 200 || request.status === 0)) {
        // store received categories
        storage.categories = JSON.parse(request.responseText).categories.categories;
        // sort categories
        storage.categories.sort();
        
        
        initializeCategories();
        refreshManagedCategories();
        applyCategoryChange();
        // load different categories in form
        loadCategoryList();
    } else if (request.readyState === 4) {
        alert("ERROR while loading categories - " + request.status);
    }
}


/* Functions for the show categories form */
function initializeCategories() {
    if (storage.events === null || storage.events.length === 0) {
        return;
    }
    // delete all previously added categories
    var categories = document.getElementById("shownCategories");
    var child = categories.lastChild;
    while (child !== null) {
        categories.removeChild(child);
        child = categories.lastElementChild;
    }
    
    // test if there is an event with no category
    if (eventWithNoCategory()) {
        // if yes add the "none" category
        addCategory(true, noneCategory);
    }
    for (var i = 0; i < storage.categories.length; i++) {
        // test if there exists an event with this category
        if (eventWithCategory(storage.categories[i])) {
            addCategory(true, storage.categories[i].name);
        }
    }
}

/**
 * Loops through all events and tests if an event has no category.
 * 
 * @returns {Boolean} True if there is an event with no category.
 */
function eventWithNoCategory() {
    for (var i = 0; i < storage.events.length; i++) {
        if (storage.events[i].categories === null || storage.events[i].categories.length === 0) {
            return true;
        }
    }
    return false;
}

/**
 * Tests if there is an event with the given category.
 * 
 * @param {type} category
 * @returns {Boolean}
 */
function eventWithCategory(category) {
    for (var i = 0; i < storage.events.length; i++) {
        if (isCategoryContainedInEvent(category.id, storage.events[i])) {
            return true;
        }
    }
    return false;
}

/**
 * 
 * @param {type} categoryId
 * @param {Event} event
 * @returns {boolean}
 */
function isCategoryContainedInEvent(categoryId, event) {
    for (var i = 0; i < event.categories.length; i++) {
        if (event.categories[i].id === categoryId) {
            return true;
        }
    }
    return false;
}

/**
 * Adds a checkbox of a category which may be shown.
 * 
 * @param {boolean} checked description
 * @param {string} name description
 * @returns {undefined}
 */
function addCategory(checked, name) {
    var checkbox = document.createElement('input');
    checkbox.type = "checkbox";
    checkbox.name = name;
    checkbox.checked = checked;
    checkbox.id = "id" + name;
    checkbox.onchange = function() {
        if (checkbox.checked) {
            showCategory(name);
        } else {
            hideCategory(name);
        }
    };
    if (checked) {
        // if initally checked append it to the show categories list
        showCategory(name);
    }
    
    var label = document.createElement('label');
    label.htmlFor = "id" + name;
    label.appendChild(document.createTextNode(name));
    
    var content = document.getElementById("shownCategories");
    content.appendChild(checkbox);
    content.appendChild(label);
    
}


/**
 * Toggles the category manager and
 * shows all categories with a delete button.
 * 
 * @returns {undefined}
 */
function manageCategories() {
    var categoryManager = document.getElementById("categoryManager");
    // toggle visibility when pressed
    if (categoryManager.style.display !== "inline-block") {
        // show
        categoryManager.style.display = "inline-block";
    } else {
        // hide
        categoryManager.style.display = "none";
        return;
    }
}


/**
 * Shows all categories with a delete button.
 * 
 */
function refreshManagedCategories() {
    // clean the list content except the title (first element)
    var content = document.getElementById("categoryManagerList");
    var title = content.firstElementChild;
    var child = content.lastElementChild;
    
    
    while (title !== child) {
        content.removeChild(child);
        child = content.lastElementChild;
    }
        
    // add all categories
    for (var i = 0; i < storage.categories.length; i++) {
        // add the category
        // create a new row
        var row = content.appendChild(document.createElement("tr"));
        // create the category column
        row.appendChild(document.createElement("td")).appendChild(document.createTextNode(storage.categories[i].name));

        // create a delete button
        var deleteButton = document.createElement("input");
        deleteButton.setAttribute("type", "button");
        deleteButton.setAttribute("value", "Delete");
        deleteButton.setAttribute("name", "deleteButton");
        deleteButton.setAttribute("onClick", "deleteCategory(" + storage.categories[i].id + ")");
        row.appendChild(document.createElement("td")).appendChild(deleteButton);
    }
    
}

/**
 * Deletes the given category of the webserver.
 * 
 * @param {type} categoryId
 * @returns {undefined}
 */
function deleteCategory(categoryId) {
    if (!confirm("Do you really want to delete the category \"" + storage.findCategory(categoryId).name + "\"?")) {
        return;
    }
    var request = new XMLHttpRequest();
    request.open("POST", URL_START, true);
    var formData = new FormData();
    formData.append("user", USER);
    formData.append("action", "delete-category");
    formData.append("format", "json");
    formData.append("id", categoryId);
    request.onreadystatechange = function() {
        alertError(request, "delete-category");
        if (request.readyState === 4 && request.status === 200) {
            // update categories after deleting
            retrieveAllAvailableEvents();
        }
    };
    request.send(formData);
}

/**
 * Close button of category manager is pressed.
 * 
 * @returns {undefined}
 */
function closeCategoryManager() {
    document.getElementById("categoryManager").style.display = "none";
}

/* Page manipulation */

/**
 * Called as the user selects another amount of items per page.
 * 
 * @param {Selection} selection
 */
function eventsPerPageChanged(selection) {
    if (selection.value === 10000) { // treat 10000 as infinity
        // extract non numeric selection
        eventsPerPage = Number.MAX_VALUE;
    } else {
        eventsPerPage = selection.value;
    }
    // save events per page
    localStorage.setItem("eventsPerPage", document.getElementById("eventsPerPage").value);
    applyCategoryChange();
    firstPage();
}

/**
 * Goes to the first page.
 */
function firstPage() {
    shownPage = 1;
    applyPage();
}

/**
 * Goes to the previous page.
 */
function previousPage() {
    shownPage--;
    if (shownPage <= 0) {
        shownPage = 1;
    }
    applyPage();
}

/**
 * Goes to the next page
 * 
 * @returns {undefined}
 */
function nextPage() {
    shownPage++;
    if (numberOfPages <= shownPage) {
        shownPage = numberOfPages;
    }
    applyPage();
}

/**
 * Goes to the last page.
 */
function lastPage() {
    shownPage = numberOfPages;
    applyPage();
}

/**
 * Shows the page which is defined by shownPage.
 */
function applyPage() {
    var pageInfo = document.getElementById("pageInfo");
    // remove child if necessary
    if (pageInfo.lastChild !== null) {
        pageInfo.removeChild(pageInfo.lastChild);
    }
    // calculate first and last item of this page
    var begin = (shownPage - 1) * eventsPerPage;
    var end = begin + parseInt(eventsPerPage);
    end = end > shownEvents.length ? shownEvents.length : end;
    
    if (shownEvents.length === 0) {
        // no events -> fix error of "Showing 1 - 0 of 0"
        pageInfo.appendChild(document.createTextNode("0 - 0 of 0"));
        return;
    }
    
    // add info text - shows which events are shown
    pageInfo.appendChild(document.createTextNode((begin + 1) + " - " + end + " of " + shownEvents.length));
    
    
    
    // clean the list content except the title (first element)
    var content = document.getElementById("listContent");
    var title = content.firstElementChild;
    var child = content.lastElementChild;
    
    
    while (title !== child) {
        content.removeChild(child);
        child = content.lastElementChild;
    }
    // add all events of the current page
    for (var i = begin; i < end && i < shownEvents.length; i++) {
        // add the event
        addEvent(shownEvents[i].event, shownEvents[i].color);
    }
}
